﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Bule.Malbilden.Data;
using Bule.Malbilden.Domain;
using Bule.Components.FilteredSelectionList;
using Bule.Malbilden.Configuration;

namespace Bule.Malbilden.Forms
{
    public partial class MainView : Form
    {
        private IPersistentContext context = new PersistentContext();
        private IListViewItemListCreator listCreator;

        Person currentUser;
        DayNutritionGoal currentGoal;

        public MainView()
        {
            InitializeComponent();

            listCreator = new ListViewItemListCreator(context);
        }

        private void MainView_Load(object sender, EventArgs e)
        {
            Initialize();
        }

        private void Initialize()
        {
            UserView userView = new UserView(context);
            userView.ShowDialog();

            currentUser = userView.SelectedUser;

            if (currentUser == null)
                Application.Exit();

            UpdateGoalTextboxes();
            UpdateEatenItemList();
            UpdateBarChart();
        }

        private void exitMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void manageMealsMenuItem_Click(object sender, EventArgs e)
        {
            MealView mealView = new MealView();
            mealView.ShowDialog();
            barChart1.Refresh();
        }

        private void manageNutritionInfoMenuItem_Click(object sender, EventArgs e)
        {
            FoodProductView foodProductView = new FoodProductView(context, listCreator);
            foodProductView.ShowDialog();
            barChart1.Refresh();
        }

        private void removeFoodButton_Click(object sender, EventArgs e)
        {
            if (eatenItemList.SelectedItems.Count > 0)
            {
                try
                {
                    IEaten item = (IEaten)eatenItemList.SelectedItems[0].Tag;
                    context.Remove((IPersistable)item);
                    currentUser.RemoveEatenItem(item);
                    eatenItemList.SelectedItems[0].Remove();
                }
                catch (NHibernate.HibernateException)
                {
                    MessageBox.Show("Livsmedlet/måltiden kunde ej tas bort.", "Fel", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                UpdateBarChart();
            }
        }

        private void addFoodButton_Click(object sender, EventArgs e)
        {
            // Let the user select what food to add:
            IEatable selectedItem = GetEatableItemToAdd();

            if (selectedItem != null)
            {
                WeightPortionsInputbox amountBox = new WeightPortionsInputbox(selectedItem.PortionName, selectedItem.PortionSize);
                DialogResult selectionResult = amountBox.ShowDialog();

                if (selectionResult == DialogResult.OK)
                {
                    double weight = amountBox.Weight;

                    IEaten eatenItem = new EatenItem() { Food = (IEatable)selectedItem };

                    eatenItem.Weight = weight;
                    eatenItem.Date = dateTimePicker.Value.Date;

                    AddEatenItem(eatenItem);
                }
            }
        }

        private void AddEatenItem(IEaten eatenItem)
        {
            try
            {
                context.Save(eatenItem);

                currentUser.AddEatenItem(eatenItem);
                context.Save(currentUser);

                AddEatenItemToList(eatenItem);

                UpdateBarChart();
            }
            catch (NHibernate.HibernateException ex)
            {
                MessageBox.Show("Livsmedlet/måltiden kunde ej läggas till." + ex.Message, "Fel", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void AddEatenItemToList(IEaten itemToAdd)
        {
            ListViewItem item = new ListViewItem();
            item.Text = itemToAdd.Name;
            item.Tag = itemToAdd;
            item.SubItems.Add(itemToAdd.Weight.ToString());
            eatenItemList.Items.Add(item);
        }

        /// <summary>
        /// Lets the user select a meal or food product.
        /// </summary>
        /// <returns>The selected item.</returns>
        private IEatable GetEatableItemToAdd()
        {
            List<string> columnHeaders = new List<string>();

            columnHeaders.Add("Namn");
            columnHeaders.Add("Typ");

            List<ListViewItem> itemList = listCreator.CreateEatableList();

            // Read selection from user:
            FilteredSelectionListDialog selector = new FilteredSelectionListDialog("Välj lisvmedel/måltid att lägga till", itemList, columnHeaders);
            DialogResult result = selector.ShowDialog();
            
            IEatable selected = null;

            if (result == DialogResult.OK && selector.SelectedItem != null)
                selected = (IEatable)selector.SelectedItem.Tag;

            return selected;
        }

        private void EatenItemList_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (eatenItemList.SelectedItems.Count > 0)
                removeFoodButton.Enabled = true;
            else
                removeFoodButton.Enabled = false;
        }

        private void dateTimePicker_ValueChanged(object sender, EventArgs e)
        {
            UpdateEatenItemList();
            UpdateGoalTextboxes();
        }

        private void foodList_DoubleClick(object sender, EventArgs e)
        {
            if (eatenItemList.SelectedItems.Count > 0)
            {
                IEaten item = (IEaten)eatenItemList.SelectedItems[0].Tag;

                // call with current weight as well, to give default value:
                WeightPortionsInputbox amountBox = new WeightPortionsInputbox(item.PortionName, item.PortionSize, item.Weight);
                DialogResult result = amountBox.ShowDialog();

                if (result == DialogResult.OK)
                {
                    double newWeight = amountBox.Weight;
                    item.Weight = newWeight;
                    context.Save(item);

                    UpdateEatenItemList();
                }
            }
        }

        private void UpdateEatenItemList()
        {
            eatenItemList.BeginUpdate();
            eatenItemList.Items.Clear();

            IEnumerable<IEaten> eatenItems = currentUser.GetEatenItem(dateTimePicker.Value.Date);

            foreach (var eatenItem in eatenItems)
                AddEatenItemToList(eatenItem);

            eatenItemList.EndUpdate();

            UpdateBarChart();
        }

        private void UpdateBarChart()
        {
            NutritionData totalNutritionalValue = currentUser.GetTotalEatenNutritionalValue(dateTimePicker.Value.Date);

            // add percentages:
            NutritionStatisticsCalculator calc = new NutritionStatisticsCalculator(currentUser, dateTimePicker.Value.Date);

            DayNutritionGoal currentGoal = currentUser.GetNutritionGoal(dateTimePicker.Value.Date);
            if (currentGoal == null)
                currentGoal = new DayNutritionGoal();

            double proteinEnergyPercentage = Math.Round(calc.GetEnergyPercentage(NutritionData.MacroNutrient.Protein));
            double carbsEnergyPercentage = Math.Round(calc.GetEnergyPercentage(NutritionData.MacroNutrient.Carbohydrates));
            double fatEnergyPercentage = Math.Round(calc.GetEnergyPercentage(NutritionData.MacroNutrient.UnsaturatedFat) +
                calc.GetEnergyPercentage(NutritionData.MacroNutrient.SaturatedFat));
            double kcal = Math.Round(totalNutritionalValue.Kcal);
            double fiber = Math.Round(totalNutritionalValue.Fiber);

            Dictionary<string, double> bars = new Dictionary<string, double>();

            bars.Add("Kcal", calc.GetKcalGoalPercentage());
            bars.Add("Prot", calc.GetProteinGoalPercentage());
            bars.Add("Kolh", calc.GetCarbGoalPercentage());
            bars.Add("Fett", calc.GetFatGoalPercentage());
            bars.Add("Fib", calc.GetFiberGoalPercentage());
            bars.Add("A", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminA, totalNutritionalValue.VitaminA));
            bars.Add("B1", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminB1, totalNutritionalValue.VitaminB1));
            bars.Add("B2", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminB2, totalNutritionalValue.VitaminB2));
            bars.Add("B3", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminB3, totalNutritionalValue.VitaminB3));
            bars.Add("B6", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminB6, totalNutritionalValue.VitaminB6));
            bars.Add("B12", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminB12, totalNutritionalValue.VitaminB12));
            bars.Add("C", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminC, totalNutritionalValue.VitaminC));
            bars.Add("D", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminD, totalNutritionalValue.VitaminD));
            bars.Add("E", calc.GetReachedPercentage(NutritionData.MicroNutrient.VitaminE, totalNutritionalValue.VitaminE));
            bars.Add("Fol", calc.GetReachedPercentage(NutritionData.MicroNutrient.FolicAcid, totalNutritionalValue.FolicAcid));
            bars.Add("Kalc", calc.GetReachedPercentage(NutritionData.MicroNutrient.Calcium, totalNutritionalValue.Calcium));
            bars.Add("Järn", calc.GetReachedPercentage(NutritionData.MicroNutrient.Iron, totalNutritionalValue.Iron));
            bars.Add("Nat", calc.GetReachedPercentage(NutritionData.MicroNutrient.Sodium, totalNutritionalValue.Sodium));
            bars.Add("Kal", calc.GetReachedPercentage(NutritionData.MicroNutrient.Potassium, totalNutritionalValue.Potassium));
            bars.Add("Fos", calc.GetReachedPercentage(NutritionData.MicroNutrient.Phosphorus, totalNutritionalValue.Phosphorus));
            bars.Add("Mag", calc.GetReachedPercentage(NutritionData.MicroNutrient.Magnesium, totalNutritionalValue.Magnesium));
            bars.Add("Zink", calc.GetReachedPercentage(NutritionData.MicroNutrient.Zinc, totalNutritionalValue.Zinc));
            bars.Add("Sel", calc.GetReachedPercentage(NutritionData.MicroNutrient.Selenium, totalNutritionalValue.Selenium));

            barChart1.BarValueLabelDictionary = bars;

            currentKcalTextbox.Text = kcal.ToString();
            currentProteinTextbox.Text = proteinEnergyPercentage.ToString();
            currentCarbTextbox.Text = carbsEnergyPercentage.ToString();
            currentFatTextbox.Text = fatEnergyPercentage.ToString();
            currentFiberTextbox.Text = fiber.ToString();
        }

        private void updateGoalsButton_Click(object sender, EventArgs e)
        {
            SaveDayGoals();
            UpdateBarChart();
        }

        private void SaveDayGoals()
        {
            int kcal = 0;

            if (!string.IsNullOrEmpty(kcalGoalTextbox.Text))
                kcal = Convert.ToInt32(kcalGoalTextbox.Text);

            currentGoal.Kcal = kcal;
            currentGoal.ProteinEnergyPercentage = Convert.ToInt32(proteinGoalTextbox.Text);
            currentGoal.CarbohydrateEnergyPercentage = Convert.ToInt32(carbGoalTextbox.Text);
            currentGoal.FatEnergyPercentage = Convert.ToInt32(fatGoalTextbox.Text);
            currentGoal.FiberWeight = Convert.ToInt32(fiberGoalTextbox.Text);

            context.Save(currentGoal);
        }

        private void UpdateGoalTextboxes()
        {
            DayNutritionGoal goal = currentUser.GetClosestGoal(dateTimePicker.Value.Date);

            if (goal == null)
            {
                INutritionGoalConfigurationReader reader = new XmlNutritionGoalConfigurationReader();
                goal = reader.GetDefaultGoal();
                goal.Date = dateTimePicker.Value.Date;
                
                context.Save(goal);
                currentUser.AddNutritionGoal(goal);

                currentGoal = goal;
            }
            else if (goal.Date != dateTimePicker.Value.Date)
            {
                currentGoal = new DayNutritionGoal(goal);
                currentGoal.Date = dateTimePicker.Value.Date;

                context.Save(currentGoal);
                currentUser.AddNutritionGoal(currentGoal);
            }
            else
                currentGoal = goal;

            kcalGoalTextbox.Text = goal.Kcal.ToString();
            proteinGoalTextbox.Text = goal.ProteinEnergyPercentage.ToString();
            carbGoalTextbox.Text = goal.CarbohydrateEnergyPercentage.ToString();
            fatGoalTextbox.Text = goal.FatEnergyPercentage.ToString();
            fiberGoalTextbox.Text = goal.FiberWeight.ToString();
        }

        private void changeUserMenuItem_Click(object sender, EventArgs e)
        {
            Initialize();
        }
    }
}
